package cn.shop.front.service.impl;

import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import cn.shop.front.entity.order.ServiceCharge;
import cn.shop.front.mapper.FrontUserMapper;
import cn.shop.front.service.AppTransactionService;
import cn.shop.front.service.BackSettingService;
import cn.shop.front.service.OrderFrozenAmountService;
import cn.shop.front.util.IpUtil;
import cn.shop.transcation.TransactionRecord.entity.TransactionRecord;
import cn.shop.transcation.backSetting.entity.BackSetting;
import cn.shop.transcation.detail.entity.FundDetail;
import cn.shop.transcation.detail.mapper.FundDetailMapper;
import cn.shop.transcation.frontUserModule.FrontUser;
import cn.shop.transcation.merchantRateModule.entity.MerchantRate;
import cn.shop.transcation.merchantRateModule.mapper.MerchantRateMapper;
import cn.shop.transcation.merchantUser.mapper.MerchantUserMapper;
import cn.shop.transcation.merchantUser.mapper.entity.MerchantUser;
import cn.shop.transcation.order.entity.Order;
import cn.shop.transcation.order.entity.OrderFrozenAmount;
import cn.shop.transcation.order.entity.OrderRate;
import cn.shop.transcation.order.entity.OrderReward;
import cn.shop.transcation.order.mapper.OrderMapper;
import cn.shop.transcation.order.mapper.OrderRateMapper;
import cn.shop.transcation.order.mapper.OrderRewardMapper;
import cn.shop.utils.enums.OrderStatus;
import cn.shop.utils.exception.ServiceException;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static cn.shop.utils.constant.SystemSettingConstant.CONVERT_TIME;

/**
 * 交易记录(TransactionRecord)表服务实现类
 *
 * @author jzw
 * @since 2021-12-13 10:49:41
 */
@Service
@Slf4j
public class AppTransactionServiceImpl implements AppTransactionService {

	@Resource
	private OrderMapper orderMapper;

	@Resource
	private MerchantUserMapper merchantUserMapper;
	@Resource
	private MerchantRateMapper merchantRateMapper;

	@Resource
	private OrderRateMapper orderRateMapper;

	@Resource
	private FrontUserMapper frontUserMapper;

	@Resource
	private FundDetailMapper fundDetailMapper;

	@Resource
	private RedissonClient redissonClient;

	@Resource
	private BackSettingService backSettingService;
	@Resource
	private OrderFrozenAmountService orderFrozenAmountService;

	@Resource
	private OrderRewardMapper rewardMapper;

	/**
	 * 创建订单记录
	 *
	 * @param transactionRecord 购买信息
	 * @return 返回结果
	 * @author jzw
	 * @since 2021/12/13 11:10
	 */
	@Override
	@Transactional(rollbackFor = Exception.class)
	public Boolean createRecord(long userId, TransactionRecord transactionRecord) {
		Order order = checkOrder(transactionRecord.getProductId(), userId);
		RLock lock = redissonClient.getLock("pay-" + transactionRecord.getProductId().toString());
		try {
			if (lock.tryLock(0, 20, TimeUnit.SECONDS)) {
				//校验商品是否为可买状态
				LocalDateTime now = LocalDateTime.now();
				Duration duration = Duration.between(now, order.getDownTime());
				//如果当前离下架时间不足2秒，则购买失败
				if (duration.getSeconds() < 2) {
					throw new ServiceException("当前商品已下架");
				}
				//校验账户余额是否充足
				FrontUser buyer = frontUserMapper.selectById(userId);
				FrontUser seller = frontUserMapper.selectById(order.getSellerId());
				if (NumberUtil.isLess(buyer.getBalance(), order.getAmount())) {
					throw new ServiceException("账户余额不足，请充值或选用其他支付方式");
				}
				// 玩家减去余额
				LambdaUpdateWrapper<FrontUser> updateUserWrapper = Wrappers.lambdaUpdate();
				updateUserWrapper.setSql("balance = balance - " + order.getAmount()).
						eq(FrontUser::getId, buyer.getId());
				boolean res = SqlHelper.retBool(frontUserMapper.update(buyer, updateUserWrapper));
				if (!res) {
					throw new ServiceException(StrUtil.format("玩家购买商品失败,扣余额出错,商品id:{},玩家id:{}", order.getId(), buyer.getId()));
				}
				Order update = new Order();
				update.setStatus(OrderStatus.COMPLETED);
				update.setBuyerId(userId);
				update.setBuyerAccount(transactionRecord.getAccount());
				update.setDealTime(LocalDateTime.now());
				update.setBuyerIp(IpUtil.getClientIp());
				update.setId(order.getId());
				order.setBuyerIp(update.getBuyerIp());
				orderMapper.updateById(update);
				processOrder(order, buyer, seller);
				return true;
			}
			else {
				throw new ServiceException("当前商品已锁定");
			}
		} catch (Exception e) {
			log.warn("余额购买异常", e);
			throw new ServiceException("购买商品失败");
		} finally {
			if (lock.isLocked() && lock.isHeldByCurrentThread()) {
				lock.unlock();
			}
		}
	}

	private void setOrderFrozenAmount(BigDecimal sellerShare, Order order, int hour) {
		LocalDateTime now = LocalDateTime.now();
		OrderFrozenAmount frozenAmount = new OrderFrozenAmount();
		frozenAmount.setAmount(sellerShare);
		frozenAmount.setOrderId(order.getId());
		frozenAmount.setUserId(order.getSellerId());
		frozenAmount.setCreateTime(now);
		frozenAmount.setDeadTime(now.plusHours(hour));

		boolean success = orderFrozenAmountService.save(frozenAmount);
		if (!success) {
			log.warn("写入转化表失败, {}", order);
			throw new ServiceException("购买失败");
		}
	}

	private FundDetail createFundDetail(FrontUser user, Order order, BigDecimal merchantShare, BigDecimal platformShare, BigDecimal sellerShare, LocalDateTime now) {
		FundDetail detail = new FundDetail();
		if (user != null) {
			detail.setPlayerAccount(user.getAccount());
			detail.setPlayerId(user.getId());
		}
		detail.setGoods(order.getGoods());
		detail.setAmount(order.getAmount());
		detail.setMerchantShare(merchantShare);
		detail.setAgencyShare(new BigDecimal("0"));
		detail.setPlatformShare(platformShare);
		detail.setSellerShare(sellerShare);
		detail.setOrderId(order.getId());
		detail.setDealTime(now);
		detail.setUid(order.getUid());
		return detail;
	}

	private Order checkOrder(long orderId, long userId) {
		LambdaQueryWrapper<Order> query = Wrappers.lambdaQuery();
		query.eq(Order::getId, orderId).eq(Order::getStatus, OrderStatus.PASSED);
		Order order = orderMapper.selectOne(query);
		if (order == null) {
			throw new ServiceException("当前商品不存在");
		}
		if (order.getSellerId() == userId) {
			throw new ServiceException("不能购买自己的商品");
		}
		return order;
	}

	private void processOrder(Order order, FrontUser buyer, FrontUser seller) {
		ServiceCharge charge = getCharge(order);
		MerchantUser merchantUser = merchantUserMapper.selectById(order.getUid());
		BigDecimal platformShare, merchantShare, sellerShare;
		BigDecimal conversionUnit = BigDecimal.valueOf(100);
		BigDecimal p_rate = NumberUtil.div(charge.getChargeRate (), conversionUnit); //平台浮动费率
		BigDecimal m_rage = NumberUtil.div(charge.getRateAdditional(), conversionUnit); //商户浮动费率
		if (charge.getSingleType() && charge.getRateType()) {//方式一，全从商户扣
			sellerShare = order.getAmount();
			platformShare = NumberUtil.mul(order.getAmount(), p_rate).add(charge.getChargeSingle());
			merchantShare = BigDecimal.ZERO;
		} else if (!charge.getRateType() && !charge.getSingleType()) { //方式二，全从玩家扣
			merchantShare =  NumberUtil.mul(order.getAmount(), m_rage).add(charge.getSingleAdditional());
			platformShare = NumberUtil.mul(order.getAmount(), p_rate).add(charge.getChargeSingle());
			sellerShare = order.getAmount().subtract(merchantShare).subtract(platformShare);
		} else if (!charge.getRateType() && charge.getSingleType()) { //方式三，金额从玩家口，单笔从商户扣
			BigDecimal m_charge = NumberUtil.mul(order.getAmount(), m_rage); //商户浮动收益
			BigDecimal p_charge = NumberUtil.mul(order.getAmount(), p_rate); //平台基本收益
			sellerShare = order.getAmount().subtract(m_charge).subtract(p_charge);
			platformShare = p_charge.add(charge.getChargeSingle());
			merchantShare = m_charge;
		} else {//方式三，金额从商户扣，单笔从玩家扣
			BigDecimal p_charge = NumberUtil.mul(order.getAmount(), p_rate); //平台基本收益
			sellerShare = order.getAmount().subtract(charge.getSingleAdditional()).subtract(charge.getChargeSingle());
			platformShare = p_charge.add(charge.getChargeSingle());
			merchantShare = charge.getSingleAdditional();
		}
		log.info("订单 {} 的结算信息：{}，总价：{}，平台收益：{}，商户收益：{}，玩家收益：{}", order, charge, order.getAmount(), platformShare, merchantShare, sellerShare);
		LocalDateTime now = LocalDateTime.now();
		//商户添加余额
		MerchantUser newMerchantUser = new MerchantUser();
		newMerchantUser.setId(merchantUser.getId());
		LambdaUpdateWrapper<MerchantUser> updateWrapper = Wrappers.lambdaUpdate(newMerchantUser);
		updateWrapper.setSql("balance = balance + " + merchantShare);
		merchantUserMapper.update(newMerchantUser, updateWrapper);
		//添加交易记录
		FundDetail buyDetail = createFundDetail(buyer, order, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, now);
		buyDetail.setType(0);
		BigDecimal balance = buyer.getBalance().subtract(order.getAmount());
		buyer.setBalance(balance);
		buyDetail.setBalance(balance);
		buyDetail.setIp(order.getBuyerIp());
		buyDetail.setPaySolution("余额支付");
		buyDetail.setAlias("www");
		fundDetailMapper.insert(buyDetail);
		FundDetail sellDetail = createFundDetail(seller, order, merchantShare, platformShare, sellerShare, now);
		sellDetail.setType(1);
		sellDetail.setBalance(seller.getBalance().add(sellerShare));
		sellDetail.setPaySolution("余额支付");
		sellDetail.setAlias("www");
		fundDetailMapper.insert(sellDetail);
		List<BackSetting> settingList = backSettingService.lambdaQuery()
				.in(BackSetting::getKey, Collections.singletonList(CONVERT_TIME))
				.list();
		Map<String, String> map = settingList.stream().collect(Collectors.toMap(BackSetting::getKey, BackSetting::getValue));
		String convertTime = map.get(CONVERT_TIME);
		if (convertTime != null && NumberUtil.parseInt(convertTime) > 0) {
			setOrderFrozenAmount(sellerShare, order, NumberUtil.parseInt(convertTime));
		} else {
			FrontUser updateUser = new FrontUser();
			updateUser.setId(order.getSellerId());
			LambdaUpdateWrapper<FrontUser> updates = Wrappers.<FrontUser>lambdaUpdate()
					.setSql("balance = balance + " + sellerShare)
					.eq(FrontUser::getId, order.getSellerId());
			int count = frontUserMapper.update(updateUser, updates);
			if (count < 1) {
				log.warn("更新卖家余额失败, {}", order);
				throw new ServiceException("购买失败");
			}
		}
		/*if (StrUtil.equals(openReward, "1")) {
			BigDecimal reward = RandomUtil
					.randomBigDecimal(new BigDecimal(rewardMin), new BigDecimal(rewardMax))
					.setScale(2, RoundingMode.UP);
			reward(buyer, order, reward);
		}*/
	}

	private void reward(FrontUser buyer, Order order, BigDecimal reward) {
		FrontUser updateUser = new FrontUser();
		updateUser.setId(buyer.getId());
		LambdaUpdateWrapper<FrontUser> updates = Wrappers.<FrontUser>lambdaUpdate()
				.setSql("balance = balance + " + reward)
				.eq(FrontUser::getId, buyer.getId());
		frontUserMapper.update(updateUser, updates);
		OrderReward r = new OrderReward();
		r.setAmount(reward);
		r.setUserId(buyer.getId());
		r.setOrderId(order.getId());
		r.setType(2);
		rewardMapper.insert(r);
		FundDetail detail = new FundDetail();
		detail.setPlayerAccount(buyer.getAccount());
		detail.setPlayerId(buyer.getId());
		detail.setGoods("app交易奖励");
		detail.setAmount(reward);
		detail.setMerchantShare(new BigDecimal("0"));
		detail.setAgencyShare(new BigDecimal("0"));
		detail.setPlatformShare(new BigDecimal("0"));
		detail.setSellerShare(new BigDecimal("0"));
		detail.setOrderId(order.getId());
		detail.setDealTime(LocalDateTime.now());
		detail.setUid(order.getUid());
		detail.setType(2);
		detail.setBalance(buyer.getBalance().add(reward));
		detail.setAlias("www");
		fundDetailMapper.insert(detail);
	}

	private ServiceCharge getCharge(Order order) {
		ServiceCharge charge = new ServiceCharge();
		OrderRate rate = orderRateMapper.selectById(order.getId());
		if (rate == null) {
			MerchantRate r = merchantRateMapper.selectById(order.getUid());
			charge.setChargeRate(r.getChargeRate());
			charge.setChargeSingle(r.getChargeSingle());
			charge.setRateAdditional(r.getRateAdditional());
			charge.setSingleAdditional(r.getSingleAdditional());
			charge.setRateType(r.getRateType());
			charge.setSingleType(r.getSingleType());
			log.info("订单费率为空，应用商户默认费率：{}, 获取的费率信息：{}", r, charge);
			return charge;
		}
		charge.setChargeRate(rate.getChargeRate());
		charge.setChargeSingle(rate.getChargeSingle());
		charge.setRateAdditional(rate.getRateAdditional());
		charge.setSingleAdditional(rate.getSingleAdditional());
		charge.setRateType(rate.getRateType());
		charge.setSingleType(rate.getSingleType());
		log.info("订单费率：{}, 获取的费率信息：{}", rate, charge);
		return charge;
	}
}

